package model.parser;

import java.awt.event.KeyEvent;
import java.util.Iterator;

import model.commands.Command;
import model.commands.CompositeCommand;
import model.commands.ScreenPointCommand;
import model.commands.flow.RandomSelect;
import model.commands.flow.Repeat;
import model.commands.flow.Wait;
import model.commands.keyboard.KeyboardMultiplePress;
import model.commands.keyboard.KeyboardPress;
import model.commands.keyboard.KeyboardType;
import model.commands.keyboard.KeyboardTypeRandomNumber;
import model.commands.keyboard.KeyboardTypeRandomText;
import model.commands.mouse.MouseClick;
import model.commands.mouse.MouseDoubleClick;
import model.commands.mouse.MouseMove;
import model.commands.mouse.MouseRightClick;
import model.commands.mouse.MouseWheel;

import org.jdom.Element;

public class CommandsPersistor {
	
	//  ---------------- GENERAL ----------------------
	public void persistCommand(Element parent,Command cmd){
		if(cmd instanceof Repeat){
			persistRepeat(parent, (Repeat)cmd);
		}else if(cmd instanceof Wait){
			persistWait(parent, (Wait)cmd);
		}else if(cmd instanceof Wait){
			persistRandomSelect(parent, (RandomSelect)cmd);
		}else if(cmd instanceof KeyboardType){
			persistKeyboardType(parent, (KeyboardType)cmd);
		}else if(cmd instanceof KeyboardPress){
			persistKeyboardPress(parent, (KeyboardPress)cmd);
		}else if(cmd instanceof KeyboardMultiplePress){
			persistKeyboardMultiplePress(parent, (KeyboardMultiplePress)cmd);
		}else if(cmd instanceof KeyboardTypeRandomNumber){
			persistKeyboardTypeRandomNumber(parent,(KeyboardTypeRandomNumber)cmd);
		}else if(cmd instanceof KeyboardTypeRandomText){
			persistKeyboardTypeRandomText(parent,(KeyboardTypeRandomText)cmd);
		}else if(cmd instanceof MouseClick){
			persistMouseClick(parent, (MouseClick)cmd);
		}else if(cmd instanceof MouseMove){
			persistMouseMove(parent, (MouseMove)cmd);
		}else if(cmd instanceof MouseRightClick){
			persistMouseRightClick(parent, (MouseRightClick)cmd);
		}else if(cmd instanceof MouseDoubleClick){
			persistMouseDoubleClick(parent, (MouseDoubleClick)cmd);
		}else if(cmd instanceof MouseWheel){
			persistMouseWheel(parent, (MouseWheel)cmd);
		}else{
			throw new RuntimeException("Unknown command: " + cmd);
		}
	}
	
	public Command loadCommand(Element element){
		switch (element.getName()) {
		case "repeat":
			return loadRepeat(element); 
		case "wait":
			return loadWait(element);
		case "randomSelect":
			return loadRandomSelect(element);
		case "keyboardType":
			return loadKeyboardType(element);
		case "keyboardPress":
			return loadKeyboardPress(element);
		case "keyboardTypeRandomNumber":
			return loadKeyboardTypeRandomNumber(element);
		case "keyboardTypeRandomText":
			return loadKeyboardTypeRandomText(element);
		case "keyboardMultiplePress":
			return loadKeyboardMultiplePress(element);
		case "mouseClick":
			return loadMouseClick(element);
		case "mouseMove":
			return loadMouseMove(element);
		case "mouseRightClick":
			return loadMouseRightClick(element);
		case "mouseDoubleClick":
			return loadMouseDoubleClick(element);
		case "mouseWheel":
			return loadMouseWheel(element);
		default:
			throw new RuntimeException("Unknown element name in script: " + element.getName());
		}
	}
	
	// ---------------- FLOW ------------------
	public void persistRepeat(Element parent,Repeat cmd){
		Element repeatElement = new Element("repeat");
		Integer times = cmd.getTimes() == null? -1 : cmd.getTimes();
		repeatElement.setAttribute("times", times.toString());
		persistInnerCommands(cmd, repeatElement);
		parent.addContent(repeatElement);
	}

	public void persistInnerCommands(CompositeCommand cmd, Element compositeCmdElement) {
		for(Command childCmd : cmd.getCommands()){
			persistCommand(compositeCmdElement, childCmd);
		}
	}
	
	public Repeat loadRepeat(Element repeatElement){
		Repeat repeat = new Repeat();
		Integer times = Integer.parseInt(repeatElement.getAttributeValue("times", "-1"));
		repeat.setParams(new Integer[]{times});
		loadInnerCommands(repeatElement, repeat);
		return repeat;
	}

	public void loadInnerCommands(Element compositeElement, CompositeCommand compositeCommand) {
		@SuppressWarnings("rawtypes")
		Iterator it = compositeElement.getChildren().iterator();
		while (it.hasNext()) {
			Element child = (Element) it.next();
			Command cmd = loadCommand(child);
			compositeCommand.addCommand(cmd);
		}
	}
	
	public void persistWait(Element parent,Wait cmd){
		Element cmdElement = new Element("wait");
		Integer ms = cmd.getMs() == null? 0 : cmd.getMs();
		cmdElement.setAttribute("ms", ms.toString());
		parent.addContent(cmdElement);
	}
	
	public Wait loadWait(Element element) {
		Wait wait = new Wait();
		Integer ms = Integer.parseInt(element.getAttributeValue("ms", "0"));
		wait.setParams(new Integer[]{ms});
		return wait;
	}
	
	public void persistRandomSelect(Element parent,RandomSelect cmd){
		Element randomSelectElement = new Element("randomSelect");
		persistInnerCommands(cmd, randomSelectElement);
		parent.addContent(randomSelectElement);	
	}
	
	public RandomSelect loadRandomSelect(Element randomSelectElement){
		RandomSelect randomSelect = new RandomSelect();
		loadInnerCommands(randomSelectElement, randomSelect);
		return randomSelect;
	}
	
	// ---------------- KEYBOARD ----------------
	public void persistKeyboardType(Element parent,KeyboardType cmd){
		Element cmdElement = new Element("keyboardType");
		cmdElement.setAttribute("text", cmd.getText());
		parent.addContent(cmdElement);
	}
	
	public KeyboardType loadKeyboardType(Element element) {
		KeyboardType keyboardType = new KeyboardType();
		keyboardType.setParams(new String[]{element.getAttributeValue("text", "")});
		return keyboardType;
	}
	
	public void persistKeyboardPress(Element parent,KeyboardPress cmd){
		Element cmdElement = new Element("keyboardPress");
		Integer keyCode = cmd.getKeyCode() == null? KeyEvent.VK_ENTER : cmd.getKeyCode();
		cmdElement.setAttribute("keyCode", keyCode.toString());
		parent.addContent(cmdElement);
	}
	
	public KeyboardPress loadKeyboardPress(Element element) {
		KeyboardPress keyboardPress = new KeyboardPress();
		Integer keyCode = Integer.parseInt(element.getAttributeValue("keyCode", String.valueOf(KeyEvent.VK_ENTER)));
		keyboardPress.setParams(new Integer[]{keyCode});
		return keyboardPress;
	}
	
	public void persistKeyboardMultiplePress(Element parent,KeyboardMultiplePress cmd){
		Element cmdElement = new Element("keyboardMultiplePress");
		Integer[] keyCodes = cmd.getKeyCodes();
		if(keyCodes==null || keyCodes.length==0){
			cmdElement.setAttribute("keyCodes", String.valueOf(KeyEvent.VK_ENTER));
		}else{
			StringBuilder strBuilder = new StringBuilder();
			for(Integer keyCode : keyCodes){
				if(strBuilder.length()>0){
					strBuilder.append(",");
				}
				strBuilder.append(keyCode);
			}
			cmdElement.setAttribute("keyCodes", strBuilder.toString());
		}
		
		parent.addContent(cmdElement);
	}
	
	public KeyboardMultiplePress loadKeyboardMultiplePress(Element element) {
		KeyboardMultiplePress keyboardMultiplePress = new KeyboardMultiplePress();
		String keyCodesAsString = element.getAttributeValue("keyCodes", String.valueOf(KeyEvent.VK_ENTER));
		String[] split = keyCodesAsString.split(",");
		Integer[] keyCodes = new Integer[split.length];
		for(int i=0; i<split.length; i++){
			keyCodes[i] = Integer.valueOf(split[i]);
		}
		keyboardMultiplePress.setParams(keyCodes);
		return keyboardMultiplePress;
	}
	
	public void persistKeyboardTypeRandomNumber(Element parent,KeyboardTypeRandomNumber cmd){
		Element cmdElement = new Element("keyboardTypeRandomNumber");
		Integer from = cmd.getFrom() == null? 0 : cmd.getFrom();
		Integer to = cmd.getTo() == null? Integer.MAX_VALUE-1 : cmd.getTo();
		cmdElement.setAttribute("from", from.toString());
		cmdElement.setAttribute("to", to.toString());
		parent.addContent(cmdElement);
	}
	
	public KeyboardTypeRandomNumber loadKeyboardTypeRandomNumber(Element element) {
		KeyboardTypeRandomNumber keyboardTypeRandomNumber = new KeyboardTypeRandomNumber();
		Integer from = Integer.parseInt(element.getAttributeValue("from", String.valueOf(0)));
		Integer to = Integer.parseInt(element.getAttributeValue("to", String.valueOf(Integer.MAX_VALUE-1)));
		keyboardTypeRandomNumber.setParams(new Integer[]{from,to});
		return keyboardTypeRandomNumber;
	}
	
	public void persistKeyboardTypeRandomText(Element parent,KeyboardTypeRandomText cmd){
		Element cmdElement = new Element("keyboardTypeRandomText");
		Integer minChars = cmd.getMinChars() == null? 10 : cmd.getMinChars();
		Integer maxChars = cmd.getMaxChars() == null? 255 : cmd.getMaxChars();
		cmdElement.setAttribute("minChars", minChars.toString());
		cmdElement.setAttribute("maxChars", maxChars.toString());
		parent.addContent(cmdElement);
	}
	
	public KeyboardTypeRandomText loadKeyboardTypeRandomText(Element element) {
		KeyboardTypeRandomText keyboardTypeRandomText = new KeyboardTypeRandomText();
		Integer minChars = Integer.parseInt(element.getAttributeValue("minChars", String.valueOf(10)));
		Integer maxChars = Integer.parseInt(element.getAttributeValue("maxChars", String.valueOf(255)));
		keyboardTypeRandomText.setParams(new Integer[]{minChars,maxChars});
		return keyboardTypeRandomText;
	}
	
	
	// ---------------- MOUSE ----------------
	public void persistMouseClick(Element parent,MouseClick cmd){
		Element cmdElement = new Element("mouseClick");
		persistXY(cmdElement,cmd);
		parent.addContent(cmdElement);
	}
	
	public MouseClick loadMouseClick(Element element) {
		MouseClick mouseClick = new MouseClick();
		loadXY(element, mouseClick);
		return mouseClick;
	}
	
	private void persistXY(Element mouseElement,ScreenPointCommand cmd){
		Integer x = cmd.getX();
		if(x!=null){
			mouseElement.setAttribute("x", x.toString());
		}
		Integer y = cmd.getY();
		if(y!=null){
			mouseElement.setAttribute("y", y.toString());
		}
		
	}
	
	private void loadXY(Element mouseElement,ScreenPointCommand cmd){
		String xStr = mouseElement.getAttributeValue("x");
		String yStr = mouseElement.getAttributeValue("y");
		if(xStr!=null && yStr!=null){
			Integer x = Integer.parseInt(xStr);
			Integer y = Integer.parseInt(yStr);
			cmd.setParams(new Integer[]{x,y});
		}
	}
	
	public void persistMouseMove(Element parent,MouseMove cmd){
		Element cmdElement = new Element("mouseMove");
		persistXY(cmdElement,cmd);
		parent.addContent(cmdElement);
	}
	
	public MouseMove loadMouseMove(Element element) {
		MouseMove mouseMove = new MouseMove();
		loadXY(element, mouseMove);
		return mouseMove;
	}
	
	public void persistMouseRightClick(Element parent,MouseRightClick cmd){
		Element cmdElement = new Element("mouseRightClick");
		persistXY(cmdElement,cmd);
		parent.addContent(cmdElement);
	}
	
	public MouseRightClick loadMouseRightClick(Element element) {
		MouseRightClick mouseRightClick = new MouseRightClick();
		loadXY(element, mouseRightClick);
		return mouseRightClick;
	}
	
	public void persistMouseDoubleClick(Element parent,MouseDoubleClick cmd){
		Element cmdElement = new Element("mouseDoubleClick");
		persistXY(cmdElement,cmd);
		parent.addContent(cmdElement);
	}
	
	public MouseDoubleClick loadMouseDoubleClick(Element element) {
		MouseDoubleClick mouseDoubleClick = new MouseDoubleClick();
		loadXY(element, mouseDoubleClick);
		return mouseDoubleClick;
	}
	
	public void persistMouseWheel(Element parent,MouseWheel cmd){
		Element cmdElement = new Element("mouseWheel");
		Integer minChars = cmd.getNotches() == null? 10 : cmd.getNotches();
		cmdElement.setAttribute("notches", minChars.toString());
		parent.addContent(cmdElement);
	}
	
	public MouseWheel loadMouseWheel(Element element) {
		MouseWheel mouseWheel = new MouseWheel();
		Integer notches = Integer.parseInt(element.getAttributeValue("notches", String.valueOf(0)));
		mouseWheel.setNotches(notches);
		return mouseWheel;
	}
	
	
}
